import React, { Suspense } from 'react';
import { connectRouter, routerMiddleware, ConnectedRouter, push, replace, go }from 'connected-react-router'
import { createBrowserHistory } from 'history'
import { Route, Switch, Link as PathLink, Redirect as PathRedirect } from 'react-router-dom'
import RouterProgress from '../components/Progress/RouterProgress'

let beforeRouterSwitchHook;

export const Link = PathLink
export const Redirect = PathRedirect
export const history = createBrowserHistory()
export const useDeforeRouterSwitch = (hook) => {
    beforeRouterSwitchHook = hook
}

const routerModule = {
    namespaced: true,
    baseReducer: connectRouter(history),
    effects: {
        async push(payload, store) {
            if (typeof beforeRouterSwitchHook === 'function') {
                beforeRouterSwitchHook(store)
            }
            store.commit(push(payload))

        },
        async replace(payload, store) {
            if (typeof beforeRouterSwitchHook === 'function') {
                beforeRouterSwitchHook(store)
            }
            store.commit(replace(payload))
        }
    },
    middlewares: [
        routerMiddleware(history),
    ]
}

export default function ReactRouter(config) {
    const { routes } = config;

    this.module = routerModule

    this.history = history

    this.push = push

    this.replace = replace

    this.go = go

    this.ConnectedRouter = ConnectedRouter

    this.useDeforeRouterSwitch = (hook) => {
        beforeRouterSwitchHook = hook
    }
    
    this.Routes = () => {
        return (
            <Suspense fallback={<RouterProgress />}>
                <RecursionRoutes routes={routes} />
            </Suspense>
        )
    }
}

function isPromise(obj) {
    return (
        !!obj &&
    (typeof obj === 'object' || typeof obj === 'function') &&
    typeof obj.then === 'function'
    );
}

function isAsyncFunction(fn) {
    if (typeof fn === 'function' && fn.constructor.toString().indexOf('AsyncFunction') !== -1) {
        return true;
    } else {
        return false;
    }
}

export function AsyncComponent({ component, access = true, redirect, ...restProps })  {
    let accessible = access
    if (typeof access === 'function' && !isAsyncFunction(access)) {
        accessible = access()
    }
    if (typeof component === 'function' || typeof accessible === 'function' || isPromise(accessible)) {
        const LazyComponent = React.lazy(async () => {
            if (typeof accessible === 'function') {
                accessible = accessible()
            }
            if (isPromise(accessible)) {
                accessible = await accessible
            }
            if (accessible) {
                if (typeof component === 'function') {
                    return component()
                } else {
                    return { default: component }
                }
            } else {
                if (redirect) {
                    const redirectPath = getRedirectPath(redirect)
                    return {default: () => <Redirect to={redirectPath} />}
                } else {
                    return {default: null}
                }
            }
        })
    
        return <LazyComponent {...restProps} />
    } else {
        if (accessible) {
            const Component = component;
            return <Component {...restProps}/>
        } else {
            if (redirect) {
                const redirectPath = getRedirectPath(redirect)
                return <Redirect to={redirectPath} />
            } else {
                return null
            }
        }
    }
}

function RecursionRoutes({ routes, basePath }) {
    return (
        <Switch>
            {routes.map((route, i) => {
                const { path, component, children, access, redirect, ...restParams } = route
                let RouteComponent = component;
                if (typeof component === 'function' || isPromise(access) || typeof access === 'function' || typeof access === 'boolean' || isPromise(access)) {
                    RouteComponent = (props) => <AsyncComponent {...{ component, access, redirect, ...props }} />
                }
                if (children && children.length > 0) {
                    const ParentComponent = RouteComponent
                    RouteComponent = (props) => {
                        return (
                            <ParentComponent {...props}>
                                <RecursionRoutes routes={children} basePath={path} />
                            </ParentComponent>
                        )
                    }
                }
                return (
                    <Route
                        key={i}
                        path={`${basePath ? basePath + '/' : ''}${path}` }
                        component={RouteComponent}
                        {...restParams}
                    />
                )
            })}
        </Switch>
    )
}

export function getRedirectPath(toPath) {
    const redirectFrom = 'redirect=' + encodeURIComponent(window.location.pathname + window.location.search)
    return toPath + (toPath.indexOf('?') === -1 ? '?' : '') + redirectFrom
}
